package com.miraclesea.module.rbac.entity;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import lombok.Getter;
import lombok.Setter;

import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.SelectBeforeUpdate;

import com.miraclesea.framework.entity.BaseOptimisticLockAuditable;
import com.miraclesea.framework.group.New;
import com.miraclesea.module.rbac.vo.Permission;
import com.miraclesea.module.rbac.vo.PermissionUtil;

@Getter
@Setter
@Entity
@Table(name = "t_role")
@DynamicInsert
@DynamicUpdate
@SelectBeforeUpdate
public final class Role extends BaseOptimisticLockAuditable<User> {
	
	private static final long serialVersionUID = 7494667783830315570L;
	
	@Column(name = "name", length = 36, nullable = false, unique = true)
	@NotNull(groups = New.class)
	@Size(min = 2, max = 36)
	private String name;
	
	@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "role")
	@Fetch(FetchMode.SUBSELECT)
	private Collection<AssignedRolePermission> assignedPermissions = new LinkedHashSet<>();
	
	@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "role")
	@Fetch(FetchMode.SUBSELECT)
	private Collection<DeniedRolePermission> deniedPermissions = new LinkedHashSet<>();
	
	public void assignPermissions(final Collection<? extends Permission> permissions) {
		for (Permission each : permissions) {
			AssignedRolePermission assignedRolePermission = new AssignedRolePermission();
			assignedRolePermission.setPermissionName(each.toPermissionString());
			assignedRolePermission.setRole(this);
			getAssignedPermissions().add(assignedRolePermission);
		}
	}
	
	public void denyPermissions(final Collection<? extends Permission> permissions) {
		for (Permission each : permissions) {
			DeniedRolePermission deniedRolePermission = new DeniedRolePermission();
			deniedRolePermission.setPermissionName(each.toPermissionString());
			deniedRolePermission.setRole(this);
			getDeniedPermissions().add(deniedRolePermission);
		}
	}
	
	public boolean hasPermission(final Permission permission) {
		return getPermissions().contains(permission);
	}
	
	private Collection<Permission> getPermissions() {
		List<Permission> result = new ArrayList<>(getAssignedPermissions().size());
		for (AssignedRolePermission each : getAssignedPermissions()) {
			result.add(PermissionUtil.from(each.getPermissionName()));
		}
		for (DeniedRolePermission each : getDeniedPermissions()) {
			result.remove(PermissionUtil.from(each.getPermissionName()));
		}
		return result;
	}
}
